﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using FakeItEasy;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.Accounts.BusinessLogic;
using Sustainalytics.Accounts.Service.Controllers;
using Sustainalytics.Accounts.Service.Models;
using Sustainalytics.AuthOperations;
using Sustainalytics.Entities;
using Sustainalytics.Utils;

namespace Sustainalytics.Accounts.Service.Tests
{
    [TestClass]
    public class AccountUsersControllerTests
    {
        private static readonly Guid AccountId = Guid.NewGuid();

        private readonly List<User> _testedUsers = new List<User>
         {
            new User
            {
                Id = Guid.NewGuid(),
                Email = "william.zombori@sustainalytics.com",
                Account = "Timisoara",
                LastName = "Zombori",
                FirstName = "William",
                AccountId = AccountId,
                Status = Status.Active,
                FullName = "William Zombori",
                ModifiedOn = DateTime.Now
            },

            new User
            {
                Id = Guid.NewGuid(),
                Email = "mihai.stanescu@sustainalytics.com",
                Account = "Arad",
                LastName = "Stanescu",
                FirstName = "Mihai",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Mihai Stanescu",
                ModifiedOn = DateTime.Now
            },
            new User
            {
                Id = Guid.NewGuid(),
                Email = "iulian.negoita@sustainalytics.com",
                Account = "Lugoj",
                LastName = "Negoita",
                FirstName = "Iulian",
                AccountId = Guid.NewGuid(),
                Status = Status.Active,
                FullName = "Iulian Negoita",
                ModifiedOn = DateTime.Now
            },
            new User
            {
                Id = Guid.NewGuid(),
                Email = "delia.cruceru@sustainalytics.com",
                Account = "Timisoara",
                LastName = "Cruceru",
                FirstName = "Delia",
                AccountId = AccountId,
                Status = Status.Inactive,
                FullName = "Delia Cruceru",
                ModifiedOn = DateTime.Now
            }
         };

        [TestMethod]
        public void Get()
        {
            var filter = new ListOptions();
            var accountsManagement = A.Fake<IAccountsManagement>();

            A.CallTo(() => accountsManagement.GetUsers(
                A<List<Guid>>.Ignored, A<Status?>.Ignored, A<String>.Ignored, A<ListOptions>.Ignored))
                .ReturnsLazily((List<Guid> accountIds, Status? status, string searchText, ListOptions listOptions) =>


            {
                    var returned = new List<User>(_testedUsers);
                    if (accountIds != null && accountIds.Count > 0)
                    {
                        returned =
                            returned.Where(c => c.AccountId.HasValue && accountIds.Contains(c.AccountId.Value)).ToList();
                    }
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned =
                            returned.Where(
                                u =>
                                    u.FullName.ToLowerInvariant().Contains(searchText) || u.Email.ToLowerInvariant().Contains(searchText) ||
                                    u.Account.ToLowerInvariant().Contains(searchText)).ToList();
                    }
                    if (listOptions != null)
                    {
                        if (listOptions.OrderBy != null && listOptions.OrderBy.Any())
                        {
                            foreach (var sortOption in listOptions.OrderBy)
                            {
                                var accountsSortBy = (UsersSortEnum) Enum.Parse(typeof(UsersSortEnum), sortOption, true);

                                switch (accountsSortBy)
                                {
                                    case UsersSortEnum.FullName:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(u => u.FullName).ToList()
                                            : returned.OrderByDescending(u => u.FullName).ToList();
                                        break;
                                    case UsersSortEnum.Email:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(u => u.Email).ToList()
                                            : returned.OrderByDescending(u => u.Email).ToList();
                                        break;
                                    case UsersSortEnum.Account:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(u => u.Account).ToList()
                                            : returned.OrderByDescending(u => u.Account).ToList();
                                        break;
                                    case UsersSortEnum.LastAccessDate:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(u => u.ModifiedOn).ToList()
                                            : returned.OrderByDescending(u => u.ModifiedOn).ToList();
                                            // the field LastAccessDate is not implemented yet
                                        break;
                                    case UsersSortEnum.Status:
                                        returned = listOptions.OrderAscending
                                            ? returned.OrderBy(u => u.Status).ToList()
                                            : returned.OrderByDescending(u => u.Status).ToList();
                                        break;
                                    default:
                                        returned = returned.OrderBy(u => u.FullName).ToList();
                                        break;
                                }
                            }
                        }
                       returned= returned.Skip(listOptions.Skip).Take(listOptions.Take).ToList();
                    }

                    return returned;
                });
            A.CallTo(()=> accountsManagement.GetUsersTotalCount(A<List<Guid>>.Ignored, A<Status?>.Ignored, A<String>.Ignored))
                .ReturnsLazily(
                (List<Guid> accountIds, Status? status, string searchText) =>
                {
                    var returned = new List<User>(_testedUsers);
                    if (accountIds != null && accountIds.Count > 0)
                    {
                        returned = returned.Where(c => c.AccountId.HasValue && accountIds.Contains(c.AccountId.Value)).ToList();
                    }
                    if (status != null)
                    {
                        returned = returned.Where(c => c.Status == status).ToList();
                    }
                    if (!string.IsNullOrWhiteSpace(searchText))
                    {
                        returned = returned.Where(u => u.FullName.ToLowerInvariant().Contains(searchText) || u.Email.ToLowerInvariant().Contains(searchText) || u.Account.ToLowerInvariant().Contains(searchText)).ToList();
                    }

                    return returned.Count;
                }
            );
            var authOperations = A.Fake<IAuthOperations>();
            using (var usersController = new UsersController(accountsManagement, authOperations))
            {
                usersController.Request = new HttpRequestMessage();
                usersController.Configuration = new HttpConfiguration();

            //Get active users by accountid
                var response = usersController.Get(filter, new List<Guid> { AccountId }, "Active");
                List<object> users;
            Assert.IsTrue(response.IsSuccessStatusCode, "The request has failed.");
                Assert.IsTrue(response.TryGetContentValue(out users), "The response does not contain the list of users.");
                Assert.AreEqual(_testedUsers.Count(u => u.AccountId == AccountId && u.Status.ToString() == "Active"), users.Count, "The number of users for the given account is different than expected.");
            }
        }

        [TestMethod]
        public void Patch()
        {
            var operations = new List<PatchOperations>
            {
                new PatchOperations
            {
                    Verb = "ResetPassword",
                    ResourceIds = new List<Guid> {Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()}
                },
                new PatchOperations
                {
                    Verb = "DeleteUser",
                    ResourceIds = new List<Guid> {Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()}
                }
            };

            var receivedUserIds = new List<Guid>();

            var accountsManagement = A.Fake<IAccountsManagement>();
            var authOperations = A.Fake<IAuthOperations>();
            A.CallTo(() => authOperations.ResetPasswordByUserIds(A<IEnumerable<Guid>>.Ignored))
                .Invokes(
                    (IEnumerable<Guid> userIds) => { receivedUserIds = userIds.ToList(); }
                );

            using (var usersController = new UsersController(accountsManagement, authOperations))
            {
                usersController.Request = new HttpRequestMessage();
                usersController.Configuration = new HttpConfiguration();

            usersController.Patch(operations);

                Assert.AreEqual(operations.Where(o => o.Verb.ToUpperInvariant().Trim() == "RESETPASSWORD").Sum(p => p.ResourceIds.Count), receivedUserIds.Count, "The returned users count is diferent than the sent users");
            Assert.AreEqual(operations.First(o => o.Verb.ToUpperInvariant().Trim() == "RESETPASSWORD").ResourceIds.First(), receivedUserIds.First(), "The received user ids is diferent than the expected one");
        }
    }
}
}
